﻿
#region Copyright & License
// --------------------------
// Copyright 2008 Daniel Gray
// --------------------------
// This file is part of Kale.
//
// Kale is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Kale is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Kale.  If not, see <http://www.gnu.org/licenses/>.
#endregion
#region File details
// --------------------------------------------------------
// Filename          : NodeProvider.cs
// Author            : Daniel Gray
// Created           : 15/04/2008
// --------------------------------------------------------
#endregion
            
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Kale.Core
{
   public abstract class NodeProvider : INodeProvider
   {
      private string _name;
      private INodeCache _cache;
      private INodeProvider _rootProvider;

      private void AssertSupportedNodeType(NodeId id)
      {
         if (id.Source != SupportedNodeSourceType) throw new UnsupportedNodeSourceException(string.Format("Do not support the node source type {0}", id.Source));
      }

      private void AssertSupportedParentNodeType(NodeId id)
      {
         if (id.Source != SupportedNodeSourceType && id.Source != SupportedParentNodeSourceType) throw new UnsupportedNodeSourceException(string.Format("Do not support the node source type {0}", id.Source));
      }

      private IImageNode CreateNode(NodeId id)
      {
         IImageNode node = null;

         if (!_cache.HasNode(id))
         {
            if (id.Source != SupportedNodeSourceType)
            {
               node = RootProvider.LoadNode(id);
            }
            else
            {
               node = CreateNodeFromId(id);
            }
            _cache.AddNode(node);
         }
         else
         {
            node = _cache[id];
         }
         return node;
      }

      protected NodeProvider(string name)
      {
         _name = name;
         _rootProvider = null;
         _cache = NodeCacheHelper.Cache;
      }

      protected NodeProvider(string name, INodeProvider rootProvider)
      {
         _name = name;
         _rootProvider = rootProvider;
         _cache = NodeCacheHelper.Cache;
      }

      protected INodeProvider RootProvider
      {
         get { return _rootProvider; }
      }

      #region Abstract methods / properties

      public abstract NodeSource SupportedNodeSourceType { get; }
      public abstract NodeSource? SupportedParentNodeSourceType { get; }
      public abstract IImageNode CreateNodeFromId(NodeId id);
      public abstract NodeId[] GetChildrenIds(NodeId id);
      protected abstract NodeId? GetParentId(NodeId id);

      #endregion

      #region INodeSource Members

      public void ClearCaches()
      {
         lock (_cache)
         {
            _cache.Clear();
         }
      }

      public IImageNode LoadNode(NodeId id)
      {
         AssertSupportedNodeType(id);

         return CreateNode(id);
      }

      public IImageNode[] LoadChildren(NodeId id)
      {
         AssertSupportedParentNodeType(id);

         List<IImageNode> childNodes = new List<IImageNode>();
         
         foreach(NodeId childId in GetChildrenIds(id))
         {
            childNodes.Add(CreateNode(childId));
         }

         return childNodes.ToArray();
      }

      public IImageNode LoadParent(NodeId id)
      {
         AssertSupportedNodeType(id);

         NodeId? parentId = GetParentId(id);

         if (parentId.HasValue)
         {
            return CreateNode(parentId.Value);
         }
         else
         {
            return null;
         }
      }

      public string Name
      {
         get { return _name; }
      }

      #endregion
   }
}
